/*
 * @copyright 2010 Gerald Jacobson
 * @license GNU General Public License
 * 
 * This file is part of My Quick Rules.
 *
 * My Quick Rules is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * My Quick Rules is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with My Quick Rules.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.amphiprion.myquickrules.screen;

import org.amphiprion.myquickrules.dao.MainRuleDao;
import org.amphiprion.myquickrules.entity.Game;
import org.amphiprion.myquickrules.entity.IGraphical;
import org.amphiprion.myquickrules.entity.Image;
import org.amphiprion.myquickrules.entity.Line;
import org.amphiprion.myquickrules.entity.MainRule;
import org.amphiprion.myquickrules.entity.Paragraph;
import org.amphiprion.myquickrules.entity.Phase;
import org.amphiprion.myquickrules.entity.Rectangle;
import org.amphiprion.myquickrules.entity.TextEntry;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.view.animation.AccelerateInterpolator;

import com.amphiprion.prototype.GameScreen;
import com.amphiprion.prototype.control.ControlerState;
import com.amphiprion.prototype.control.ControlerState.ClickMode;

/**
 * @author Amphiprion
 * 
 */
public class GameRulesScreen extends GameScreen {
	private static AccelerateInterpolator accelerator = new AccelerateInterpolator();
	// private Game game;
	private int currentPhase;
	private MainRule mainRule;
	private float screenRotation;
	private float dpiScale = 0;
	private static float SCREEN_WIDTH = 480;
	private static float SCREEN_HEIGHT = 800;
	private Paint paint = new Paint();
	private Paint paintText = new Paint();
	private Paint paintLine = new Paint();
	private Paint paintFill = new Paint();

	private enum OverviewState {
		OPEN, CLOSE, OPENING, CLOSING
	}

	private OverviewState overviewState;
	private float offsetOverview = 0;
	private float offsetOverviewClose;
	private float offsetOverviewOpen = 0;

	private long elapsedOverviewAnim;
	private float offsetOverviewStart;
	private float lastMoveOverview;
	private boolean moveOverview;
	private float delayOverviewAnim;

	private float offsetPhase;

	private enum PhaseState {
		NONE, TO_NEXT, TO_PREVIOUS, TO_CURRENT
	}

	private PhaseState phaseState = PhaseState.NONE;
	private long elapsedPhaseAnim;
	private float offsetPhaseStart;
	private float delayPhaseAnim;
	private float lastMovePhase;
	private boolean movePhase;

	/**
	 * @param context
	 */
	public GameRulesScreen(Context context, Game game) {
		this(context, MainRuleDao.getInstance().getMainRule(game.getId()));
	}

	/**
	 * @param context
	 */
	public GameRulesScreen(Context context, MainRule mainRule) {
		super(context);
		this.mainRule = mainRule;

		SCREEN_WIDTH = mainRule.getWidth();
		SCREEN_HEIGHT = mainRule.getHeight();

		currentPhase = 0;
		if (mainRule.getOverview() != null) {
			offsetOverviewOpen = 0;
			if (mainRule.getOverview().getBitmap() != null) {
				offsetOverviewClose = -mainRule.getOverview().getBitmap().getHeight() + mainRule.getOverview().visibleHeight;
			} else {
				offsetOverviewClose = -SCREEN_HEIGHT + mainRule.getOverview().visibleHeight;
			}
			if (mainRule.getPhases() != null && mainRule.getPhases().size() > 0) {
				overviewState = OverviewState.CLOSE;
				offsetOverview = offsetOverviewClose;
			} else {
				overviewState = OverviewState.OPEN;
				offsetOverview = offsetOverviewOpen;
			}
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void onInput(ControlerState state, long elpased) {
		// overview management
		if (mainRule.getOverview() != null && phaseState == PhaseState.NONE) {
			if (state.click == ClickMode.DOWN) {
				if (overviewState == OverviewState.OPEN) {
					moveOverview = true;
				} else if (overviewState == OverviewState.CLOSE) {
					if (screenRotation == 0 && state.position.y < 50 * dpiScale || screenRotation != 0 && getWidth() - state.position.x < 50 * dpiScale) {
						moveOverview = true;
					}
				}
			} else if (state.click == ClickMode.UP && moveOverview) {
				moveOverview = false;
				elapsedOverviewAnim = 0;
				offsetOverviewStart = offsetOverview;
				if (screenRotation == 0) {
					delayOverviewAnim = 750.0f;
				} else {
					delayOverviewAnim = 500.0f;
				}
				if (lastMoveOverview > 0) {
					overviewState = OverviewState.OPENING;
					delayOverviewAnim = delayOverviewAnim * (offsetOverviewOpen - offsetOverview) / (offsetOverviewOpen - offsetOverviewClose);
				} else {
					overviewState = OverviewState.CLOSING;
					delayOverviewAnim = delayOverviewAnim * (offsetOverview - offsetOverviewClose) / (offsetOverviewOpen - offsetOverviewClose);
				}
			}
			if (overviewState == OverviewState.OPENING) {
				elapsedOverviewAnim += elpased;
				if (elapsedOverviewAnim < delayOverviewAnim) {
					float v = accelerator.getInterpolation(elapsedOverviewAnim / delayOverviewAnim);
					offsetOverview = offsetOverviewStart + (offsetOverviewOpen - offsetOverviewStart) * v;
				} else {
					offsetOverview = offsetOverviewOpen;
					overviewState = OverviewState.OPEN;
				}
			} else if (overviewState == OverviewState.CLOSING) {
				elapsedOverviewAnim += elpased;
				if (elapsedOverviewAnim < delayOverviewAnim) {
					float v = accelerator.getInterpolation(elapsedOverviewAnim / delayOverviewAnim);
					offsetOverview = offsetOverviewStart + (offsetOverviewClose - offsetOverviewStart) * v;
				} else {
					offsetOverview = offsetOverviewClose;
					overviewState = OverviewState.CLOSE;
				}
			}

			if (moveOverview) {
				float mv = 0;
				if (screenRotation == 0) {
					mv = state.translate.y;
				} else {
					mv = -state.translate.x;
				}
				if (mv != 0) {
					lastMoveOverview = mv;
					offsetOverview += lastMoveOverview;
					offsetOverview = Math.max(offsetOverviewClose, offsetOverview);
					offsetOverview = Math.min(offsetOverviewOpen, offsetOverview);
				}
			}
		}

		// phase managment
		if (mainRule.getPhases() != null) {
			if (moveOverview == false) {
				if (state.click == ClickMode.DOWN) {
					movePhase = true;
				} else if (state.click == ClickMode.UP && movePhase) {
					movePhase = false;
					elapsedPhaseAnim = 0;
					offsetPhaseStart = offsetPhase;
					if (screenRotation == 0) {
						delayPhaseAnim = 500.0f;
					} else {
						delayPhaseAnim = 750.0f;
					}
					if (offsetPhase >= SCREEN_WIDTH / 4 && (mainRule.loopOnPhase || currentPhase > 0)) {
						phaseState = PhaseState.TO_PREVIOUS;
						delayPhaseAnim = delayPhaseAnim * (SCREEN_WIDTH - offsetPhase) / SCREEN_WIDTH;
					} else if (offsetPhase <= -SCREEN_WIDTH / 4 && (mainRule.loopOnPhase || currentPhase + 1 < mainRule.getPhases().size())) {
						phaseState = PhaseState.TO_NEXT;
						delayPhaseAnim = delayPhaseAnim * (SCREEN_WIDTH + offsetPhase) / SCREEN_WIDTH;
					} else {
						phaseState = PhaseState.TO_CURRENT;
						delayPhaseAnim = delayPhaseAnim * Math.abs(offsetPhase) / SCREEN_WIDTH;
					}

				}
				if (movePhase) {
					float mv = 0;
					if (screenRotation == 0) {
						mv = state.translate.x;
					} else {
						mv = state.translate.y;
					}
					if (mv != 0) {
						lastMovePhase = mv;
						offsetPhase += lastMovePhase;
						offsetPhase = Math.max(-SCREEN_WIDTH, offsetPhase);
						offsetPhase = Math.min(SCREEN_WIDTH, offsetPhase);
					}
				}
			}

			if (phaseState == PhaseState.TO_NEXT) {
				elapsedPhaseAnim += elpased;
				if (elapsedPhaseAnim < delayPhaseAnim) {
					float v = accelerator.getInterpolation(elapsedPhaseAnim / delayPhaseAnim);
					offsetPhase = offsetPhaseStart - (SCREEN_WIDTH + offsetPhaseStart) * v;
				} else {
					offsetPhase = 0;
					phaseState = PhaseState.NONE;
					currentPhase = (currentPhase + 1) % mainRule.getPhases().size();
				}
			} else if (phaseState == PhaseState.TO_PREVIOUS) {
				elapsedPhaseAnim += elpased;
				if (elapsedPhaseAnim < delayPhaseAnim) {
					float v = accelerator.getInterpolation(elapsedPhaseAnim / delayPhaseAnim);
					offsetPhase = offsetPhaseStart + (SCREEN_WIDTH - offsetPhaseStart) * v;
				} else {
					offsetPhase = 0;
					phaseState = PhaseState.NONE;
					currentPhase = currentPhase - 1;
					if (currentPhase < 0) {
						currentPhase = mainRule.getPhases().size() - 1;
					}
				}
			} else if (phaseState == PhaseState.TO_CURRENT) {
				elapsedPhaseAnim += elpased;
				if (elapsedPhaseAnim < delayPhaseAnim) {
					float v = accelerator.getInterpolation(elapsedPhaseAnim / delayPhaseAnim);
					offsetPhase = offsetPhaseStart - offsetPhaseStart * v;
				} else {
					offsetPhase = 0;
					phaseState = PhaseState.NONE;
				}
			}
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void onDraw(Canvas canvas) {

		if (SCREEN_WIDTH < SCREEN_HEIGHT) {
			if (canvas.getWidth() < canvas.getHeight()) {
				screenRotation = 0;
				dpiScale = Math.min(canvas.getWidth() / SCREEN_WIDTH, canvas.getHeight() / SCREEN_HEIGHT);
			} else {
				screenRotation = 90;
				dpiScale = Math.min(canvas.getHeight() / SCREEN_WIDTH, canvas.getWidth() / SCREEN_HEIGHT);
			}
		} else {
			if (canvas.getWidth() < canvas.getHeight()) {
				screenRotation = 90;
				dpiScale = Math.min(canvas.getHeight() / SCREEN_WIDTH, canvas.getWidth() / SCREEN_HEIGHT);
			} else {
				screenRotation = 0;
				dpiScale = Math.min(canvas.getWidth() / SCREEN_WIDTH, canvas.getHeight() / SCREEN_HEIGHT);
			}
		}
		if (mainRule.getPhases() != null && mainRule.getPhases().size() > 0) {
			if (currentPhase >= mainRule.getPhases().size()) {
				currentPhase = mainRule.getPhases().size() - 1;
			}
			Phase phase = mainRule.getPhases().get(currentPhase);
			phase.setDisplayed(mainRule.id, true);
			drawPhase(canvas, phase, offsetPhase);

			Phase otherPhase = null;
			if (offsetPhase < 0) {
				if (currentPhase + 1 >= mainRule.getPhases().size()) {
					if (mainRule.loopOnPhase) {
						otherPhase = mainRule.getPhases().get(0);
					}
				} else {
					otherPhase = mainRule.getPhases().get(currentPhase + 1);
				}
				if (otherPhase != null) {
					otherPhase.setDisplayed(mainRule.id, true);
					drawPhase(canvas, otherPhase, offsetPhase + SCREEN_WIDTH);
				}
			} else if (offsetPhase > 0) {
				if (currentPhase == 0) {
					if (mainRule.loopOnPhase) {
						otherPhase = mainRule.getPhases().get(mainRule.getPhases().size() - 1);
					}
				} else {
					otherPhase = mainRule.getPhases().get((currentPhase - 1) % mainRule.getPhases().size());
				}
				if (otherPhase != null) {
					otherPhase.setDisplayed(mainRule.id, true);
					drawPhase(canvas, otherPhase, offsetPhase - SCREEN_WIDTH);
				}
			}
			for (Phase ppp : mainRule.getPhases()) {
				if (ppp != phase && ppp != otherPhase) {
					ppp.setDisplayed(mainRule.id, false);
				}
			}
		}

		if (mainRule.getOverview() != null) {
			if (mainRule.getOverview().getBitmap() != null) {
				offsetOverviewClose = -mainRule.getOverview().getBitmap().getHeight() + mainRule.getOverview().visibleHeight;
				if (overviewState == OverviewState.CLOSE) {
					offsetOverview = offsetOverviewClose;
				}
				drawImage(canvas, mainRule.getOverview().getBitmap(), mainRule.getOverview().getBitmap().getWidth() / 2, offsetOverview
						+ mainRule.getOverview().getBitmap().getHeight() / 2, 1.0f);
			} else {
				offsetOverviewClose = -SCREEN_HEIGHT + mainRule.getOverview().visibleHeight;
			}
			if (mainRule.getOverview().getParagraphs() != null) {
				for (Paragraph pr : mainRule.getOverview().getParagraphs()) {
					float x = pr.x;
					float y = pr.y;
					for (TextEntry te : pr.textEntries) {
						paintText.setTextSize(te.size);
						paintText.setAntiAlias(true);
						paintText.setFakeBoldText(te.bold);
						paintText.setColor(mainRule.getColor(te.color));
						String[] txts = te.text.split("\\\\n");

						for (int i = 0; i < txts.length; i++) {
							y += te.size;
							drawText(canvas, txts[i], x, offsetOverview + y, pr.alignment, paintText);
							if (i + 1 < txts.length) {
								y += te.spacing;
							}
						}
						y += pr.spacing;
					}
				}
			}
		}
	}

	private void drawPhase(Canvas canvas, Phase phase, float offset) {
		if (phase == null) {
			return;
		}
		drawImage(canvas, phase.bitmap, SCREEN_WIDTH / 2 + offset, SCREEN_HEIGHT / 2, 1.0f);
		if (phase.graphicals != null) {
			for (IGraphical gr : phase.graphicals) {
				if (gr instanceof Line) {
					Line ln = (Line) gr;
					paintLine.setColor(mainRule.getColor(ln.color));
					paintLine.setStrokeWidth(ln.width);
					// paintLine.setStyle(Style.STROKE);
					drawLine(canvas, ln.x1 + offset, ln.y1, ln.x2 + offset, ln.y2, paintLine);
				} else if (gr instanceof Rectangle) {
					Rectangle rt = (Rectangle) gr;

					if (rt.filled) {
						paintFill.setColor(mainRule.getColor(rt.filledColor));
						drawFillRect(canvas, rt.left + offset, rt.top, rt.right + offset, rt.bottom, paintFill);
					}

					paintLine.setColor(mainRule.getColor(rt.color));
					paintLine.setStrokeWidth(rt.width);
					drawRect(canvas, rt.left + offset, rt.top, rt.right + offset, rt.bottom, paintLine);
				} else if (gr instanceof Image) {
					Image im = (Image) gr;
					drawImage(canvas, im.bitmap, im.left + offset, im.top, im.bitmap.getWidth(), im.bitmap.getHeight());
				}
			}
		}
		if (phase.paragraphs != null) {
			for (Paragraph pr : phase.paragraphs) {
				float x = pr.x;
				float y = pr.y;
				for (TextEntry te : pr.textEntries) {
					paintText.setTextSize(te.size);
					paintText.setAntiAlias(true);
					paintText.setFakeBoldText(te.bold);
					paintText.setColor(mainRule.getColor(te.color));
					String[] txts = te.text.split("\\\\n");

					for (int i = 0; i < txts.length; i++) {
						y += te.size;
						drawText(canvas, txts[i], x + offset, y, pr.alignment, paintText);
						if (i + 1 < txts.length) {
							y += te.spacing;
						}
					}
					y += pr.spacing;
				}
			}
		}
	}

	private void drawImage(Canvas canvas, Bitmap img, float centerX, float centerY, float scale) {
		drawImage(canvas, img, centerX, centerY, scale, paint);
	}

	private void drawImage(Canvas canvas, Bitmap img, float centerX, float centerY, float scale, Paint paint) {
		if (img == null) {
			return;
		}
		Matrix m = new Matrix();
		m.postTranslate(-img.getWidth() / 2, -img.getHeight() / 2);
		m.postRotate(screenRotation);
		m.postScale(scale * dpiScale, scale * dpiScale);
		if (screenRotation == 0) {
			m.postTranslate(centerX * dpiScale, centerY * dpiScale);
		} else {
			m.postTranslate(canvas.getWidth() - centerY * dpiScale, centerX * dpiScale);
		}
		canvas.drawBitmap(img, m, paint);
	}

	private void drawImage(Canvas canvas, Bitmap img, float x, float y, float width, float height) {
		if (img == null) {
			return;
		}
		Matrix m = new Matrix();
		m.postTranslate(-img.getWidth() / 2, -img.getHeight() / 2);
		m.postRotate(screenRotation);
		m.postScale(dpiScale, dpiScale);
		if (screenRotation == 0) {
			m.postTranslate(x * dpiScale + img.getWidth() / 2 * dpiScale, y * dpiScale + img.getHeight() / 2 * dpiScale);
		} else {
			m.postTranslate(canvas.getWidth() - (y * dpiScale + img.getHeight() / 2 * dpiScale), x * dpiScale + img.getWidth() / 2 * dpiScale);
		}
		canvas.drawBitmap(img, m, paint);
	}

	private void drawLine(Canvas canvas, float x1, float y1, float x2, float y2, Paint paint) {
		float pSize = paint.getStrokeWidth();
		paint.setStrokeWidth(pSize * dpiScale);
		Matrix m = new Matrix();
		// m.postTranslate(-canvas.getWidth() / 2, -canvas.getHeight() / 2);
		m.postRotate(screenRotation);
		if (screenRotation == 0) {
			// m.postTranslate(canvas.getWidth() / 2, canvas.getHeight() / 2);
		} else {
			m.postTranslate(canvas.getWidth(), 0);
		}
		canvas.save();
		canvas.setMatrix(m);
		canvas.drawLine(x1 * dpiScale, y1 * dpiScale, x2 * dpiScale, y2 * dpiScale, paint);
		canvas.restore();
		paint.setStrokeWidth(pSize);
	}

	private void drawRect(Canvas canvas, float left, float top, float right, float bottom, Paint paint) {
		float pSize = paint.getStrokeWidth();
		paint.setStrokeWidth(pSize * dpiScale);
		paint.setStyle(Style.STROKE);

		Matrix m = new Matrix();
		// m.postTranslate(-canvas.getWidth() / 2, -canvas.getHeight() / 2);
		m.postRotate(screenRotation);
		if (screenRotation == 0) {
			// m.postTranslate(canvas.getWidth() / 2, canvas.getHeight() / 2);
		} else {
			m.postTranslate(canvas.getWidth(), 0);
		}
		canvas.save();
		canvas.setMatrix(m);
		canvas.drawRect(left * dpiScale, top * dpiScale, right * dpiScale, bottom * dpiScale, paint);
		canvas.restore();
		paint.setStrokeWidth(pSize);
	}

	private void drawFillRect(Canvas canvas, float left, float top, float right, float bottom, Paint paint) {
		paint.setStyle(Style.FILL);

		Matrix m = new Matrix();
		// m.postTranslate(-canvas.getWidth() / 2, -canvas.getHeight() / 2);
		m.postRotate(screenRotation);
		if (screenRotation == 0) {
			// m.postTranslate(canvas.getWidth() / 2, canvas.getHeight() / 2);
		} else {
			m.postTranslate(canvas.getWidth(), 0);
		}
		canvas.save();
		canvas.setMatrix(m);
		canvas.drawRect(left * dpiScale, top * dpiScale, right * dpiScale, bottom * dpiScale, paint);
		canvas.restore();

	}

	private void drawText(Canvas canvas, String str, float x, float y, String alignment, Paint paint) {
		float pSize = paint.getTextSize();
		if ("center".equals(alignment)) {
			x -= paint.measureText(str) / 2;
		}
		paint.setTextSize(pSize * dpiScale);
		Matrix m = new Matrix();
		// m.postTranslate(-canvas.getWidth() / 2, -canvas.getHeight() / 2);
		m.postRotate(screenRotation);
		if (screenRotation == 0) {
			// m.postTranslate(canvas.getWidth() / 2, canvas.getHeight() / 2);
		} else {
			m.postTranslate(canvas.getWidth(), 0);
		}
		float txtX;
		float txtY;
		if (screenRotation == 0) {
			txtX = x * dpiScale;
			txtY = y * dpiScale;
		} else {
			txtX = x * dpiScale;
			txtY = y * dpiScale;
		}

		canvas.save();
		canvas.setMatrix(m);
		canvas.drawText(str, txtX, txtY, paint);
		// canvas.drawRect(0, 0, 400, 240, paint);
		canvas.restore();
		paint.setTextSize(pSize);

	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void onDestroy() {
		if (mainRule.getOverview() != null) {
			mainRule.getOverview().setBitmap(null);
		}
		if (mainRule.getPhases() != null) {
			for (Phase p : mainRule.getPhases()) {
				p.bitmap = null;
			}
		}
		System.gc();
	}
}
